/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package facade;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.ejb.EJB;
import local.ComputeSessionLocal;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import local.AttemptFacadeLocal;
import local.ConfigurationFacadeLocal;
import local.IndustryFacadeLocal;
import local.PublicUserFacadeLocal;
import local.QuestionFacadeLocal;
import local.RoleFacadeLocal;
import local.ScoringFacadeLocal;
import local.ThemeFacadeLocal;
import local.ThemeScoreFacadeLocal;
import model.Attempt;
import model.Configuration;
import model.PublicUser;
import model.Question;
import model.Recommendation;
import model.Role;
import model.Scoring;
import model.Theme;
import model.ThemeScore;
import util.report.ChartsHelper;
import util.report.SubChartHelper;

/**
 *
 * @author yingzhang
 */
@Stateless
public class ComputeSession implements ComputeSessionLocal {

    @EJB
    IndustryFacadeLocal industryFacade;
    @EJB
    ThemeFacadeLocal themeFacade;
    @EJB
    AttemptFacadeLocal attemptFacade;
    @EJB
    PublicUserFacadeLocal publicUserFacade;
    @EJB
    ScoringFacadeLocal scoringFacade;
    @EJB
    ThemeScoreFacadeLocal themeScoreFacade;
    @EJB
    QuestionFacadeLocal questionFacade;
    @EJB
    RoleFacadeLocal roleFacade;
    @EJB
    ConfigurationFacadeLocal configurationFacade;
    @PersistenceContext(unitName = "IEMPPU")
    private EntityManager em;

    public enum ComputeType {

        Average, Total
    }

    public enum SF1IndexType {

        AvgIndivAvgSc("Average score based on 45 surveyed questions"),
        AvgIndivFirmSc("What score I ranked the theme for my firm(average)"),
        AvgIndivIndustrySc("What score I ranked the theme for my industry(average)"),
        AvgAllAvgSc("Average score based on 45 questions from all acceptable surveys"),
        AvgAllFirmSc("Average firm score based on all survey's firm ranking"),
        AvgAllIndustrySc("Average industry score based on all survey's industry ranking"),
        TotalIndivAvgSc("Total score based on 45 surveyed questions"),
        TotalIndivFirmSc("What score I ranked the theme for my firm(sum)"),
        TotalIndivIndustrySc("What score I ranked the theme for my industry(sum)"),
        TotalAllAvgSc("Total score based on 45 questions from all acceptable surveys"),
        TotalAllFirmSc("Total firm score based on all survey's firm ranking"),
        TotalAllIndustrySc("Total industry score based on all survey's industry ranking");
        private String text;

        SF1IndexType(String text) {
            this.text = text;
        }

        public String getText() {
            return this.text;
        }

        public static SF1IndexType fromString(String text) {
            if (text != null) {
                for (SF1IndexType p : SF1IndexType.values()) {
                    if (text.equalsIgnoreCase(p.text)) {
                        return p;
                    }
                }
            }
            return null;
        }
    }

    public enum SF2IndexType {

        WeightedWRTAvgIndivAvgSc("Weighted average score using current survey's score ranking"),
        WeightedWRTAvgIndivFirmSc("Weighted average score using current survey's firm priority");
        private String text;

        SF2IndexType(String text) {
            this.text = text;
        }

        public String getText() {
            return this.text;
        }

        public static SF2IndexType fromString(String text) {
            if (text != null) {
                for (SF2IndexType p : SF2IndexType.values()) {
                    if (text.equalsIgnoreCase(p.text)) {
                        return p;
                    }
                }
            }
            return null;
        }
    }
    public static final String RankGraph1 = "My Firm Ranking";
    public static final String RankGraph2 = "My Industry Ranking";
    public static final String RankGraph3 = "My Mixed Ranking F:I = 1:3";
    public static final String RankGraph4 = "My Mixed Ranking F:I = 1:1";
    public static final String RankGraph5 = "My Mixed Ranking F:I = 3:1";
    public static final String RankGraphBM = "My Firm Ranking with Benchmarking";

    // method to sort map entry to desc
    static <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValuesDesc(Map<K, V> map) {
        SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<Map.Entry<K, V>>(
                new Comparator<Map.Entry<K, V>>() {

                    @Override
                    public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
                        int res = e2.getValue().compareTo(e1.getValue());
                        return res != 0 ? res : 1;
                    }
                });
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

    // method to sort map entry to asc
    static <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValuesAsc(Map<K, V> map) {
        SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<Map.Entry<K, V>>(
                new Comparator<Map.Entry<K, V>>() {

                    @Override
                    public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
                        int res = e1.getValue().compareTo(e2.getValue());
                        return res != 0 ? res : 1;
                    }
                });
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

    @Override
    public double[][] computeIndivFourFourSet(Attempt att) {
        // init result
        double[][] result = new double[4][4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result[i][j] = 0;
            }
        }

        // get the latest attempt
        List<Scoring> attScores = (List<Scoring>) att.getScoring(); // 45 scores for this attempt

        HashMap themeScoreMap = new HashMap();
        // scoring, get theme into hashmap
        for (Scoring sc : attScores) {
            Theme t = em.find(Theme.class, sc.getTheme_id());
            if (themeScoreMap.get(t.getId()) == null) {
                // insert new key with list
                ArrayList<Scoring> scs = new ArrayList<Scoring>();
                scs.add(sc);
                themeScoreMap.put(t.getId(), scs);
            } else {
                // insert into original list
                ArrayList<Scoring> scs = (ArrayList<Scoring>) themeScoreMap.get(t.getId());
                scs.add(sc);
                themeScoreMap.put(t.getId(), scs);
            }
        }

        // TODO: testing for hashmap
//        System.out.println("print out the hashmap" + themeScoreMap);

        int[] lengths = new int[9];
        int totalNumberQns = 0;
        for (Object keyId : themeScoreMap.keySet()) {
            Long theme_id = (Long) keyId;
            ArrayList<Scoring> scs = (ArrayList<Scoring>) themeScoreMap.get(theme_id);

            int sum = 0;
            for (Scoring sc : scs) {
                sum += sc.getScore();
            }

            int x = 0, y = 0;
            int normedThemeId = (theme_id.intValue() - 1) % 9;
            y = normedThemeId / 3;
            x = normedThemeId % 3;

            result[x][y] = sum * 1.0 / scs.size();

            result[3][y] += sum;
            result[x][3] += sum;
            result[3][3] += sum;

            lengths[normedThemeId] = scs.size();
            totalNumberQns += scs.size();
        }

        // compute the aggregated average value
        for (int i = 0; i < 3; i++) {
            result[i][3] = result[i][3] / (lengths[0 + i % 3] + lengths[3 + i % 3] + lengths[6 + i % 3]);
            result[3][i] = result[3][i] / (lengths[0 + i * 3] + lengths[1 + i * 3] + lengths[2 + i * 3]);
        }
        result[3][3] = result[3][3] / totalNumberQns;

        // TODO: testing for result show
//        for (int i = 0; i < 4 ; i++) {
//            for (int j = 0; j < 4; j++) {
//                System.out.println("value i = " + i + " j : "+ j + "   "+ result[i][j]);
//            }
//        }

        // returned result is 4*4 2D array with {1, 2, 3, 1-3; 4, 5, 6, 4-6; 7, 8, 9, 7-9...}
        return result;
    }

    @Override
    public void generateComputeTestData() {
        int randomQty = 50;
        Random randomGenerator = new Random();
        for (int i = 0; i < randomQty; i++) {
            PublicUser pu = new PublicUser();
            Attempt att = new Attempt();

            att.setCountry("Singapore");
            Integer industry_id = randomGenerator.nextInt(3) + 1;
            att.setIndustry(industryFacade.find(industry_id.intValue())); // generate from 1 to 3
            attemptFacade.create(att);

            Role r = roleFacade.findRole("Public User");
            pu.setRole(r);
            pu = publicUserFacade.create(pu);

            // update theme related
            for (int j = 0; j < 9; j++) {
                // get the theme
                Query query = em.createQuery("SELECT t FROM Theme t WHERE t.industry_id=:p1 AND t.position=:p2");
                query.setParameter("p1", industry_id);
                query.setParameter("p2", j + 1);

                Theme th = (Theme) query.getSingleResult();

                // create the theme score
                ThemeScore ts = new ThemeScore();

                // set industry score in theme score
                int ansInd = randomGenerator.nextInt(5) + 1;
                ts.setTheme_id(th.getId());
                ts.setInd_score(ansInd);
                ts.setTheme(th);
                themeScoreFacade.create(ts);

                att.getThemeScore().add(ts);
                attemptFacade.edit(att);

                // set firm score in theme score
                int ansFirm = randomGenerator.nextInt(5) + 1;
                ts.setFirm_score(ansFirm);
                themeScoreFacade.edit(ts);

                // survey questions
                Configuration config = configurationFacade.find(4);//Randomize survey question

                for (Question qn : th.getQuestion()) {
                    qn.setTheme_id(th.getTheme_id());

                    int scoreAns = randomGenerator.nextInt(5) + 1;

                    Scoring sc = new Scoring();
                    sc.setTheme_id(qn.getTheme_id());
                    sc.setScore(scoreAns);
                    sc.setQuestion(qn);
                    scoringFacade.create(sc);

                    att.getScoring().add(sc);
                    att.setStatus(Attempt.SurveyStatus.Acceptable);
                    attemptFacade.edit(att);

                }
            }

            double[][] averageScores = computeIndivFourFourSet(att);
            for (ThemeScore ts : att.getThemeScore()) {

                switch (((Theme) themeFacade.find(ts.getTheme_id())).getPosition()) {

                    case 1:
                        ts.setAvg_score(averageScores[0][0]);
                        break;
                    case 2:
                        ts.setAvg_score(averageScores[0][1]);
                        break;
                    case 3:
                        ts.setAvg_score(averageScores[0][2]);
                        break;
                    case 4:
                        ts.setAvg_score(averageScores[1][0]);
                        break;
                    case 5:
                        ts.setAvg_score(averageScores[1][1]);
                        break;
                    case 6:
                        ts.setAvg_score(averageScores[1][2]);
                        break;
                    case 7:
                        ts.setAvg_score(averageScores[2][0]);
                        break;
                    case 8:
                        ts.setAvg_score(averageScores[2][1]);
                        break;
                    case 9:
                        ts.setAvg_score(averageScores[2][2]);
                        break;
                }
                themeScoreFacade.edit(ts);
            }


            pu.getAttempt().add(att);
            publicUserFacade.edit(pu);

        }
    }

    @Override
    public Map<String, double[][]> superComputeOne(Attempt att) {
        Map<String, double[][]> result = new HashMap<String, double[][]>();

        // ======== aggregate data ========
        // **step1** 
        //raw data for individual avg theme scores (based on 45 qns)
        Map<Long, ArrayList<Double>> oneFirmThemeScore = new HashMap<Long, ArrayList<Double>>();
        Map<Long, ArrayList<Double>> oneIndustryThemeScore = new HashMap<Long, ArrayList<Double>>();
        Map<Long, ArrayList<Double>> oneAvgThemeScore = new HashMap<Long, ArrayList<Double>>();

        for (ThemeScore ts : att.getThemeScore()) {
            long tsThemeId = ts.getTheme().getId();
            // **step2.1**
            //raw data for one firm theme scores (based on 9 firm qns)
            if (oneFirmThemeScore.get(tsThemeId) == null) {
                // insert
                ArrayList<Double> forOneTheme = new ArrayList<Double>();
                forOneTheme.add(ts.getFirm_score().doubleValue());
                oneFirmThemeScore.put(tsThemeId, forOneTheme);
            } else {
                // modify
                ArrayList<Double> forOneTheme = oneFirmThemeScore.get(tsThemeId);
                forOneTheme.add(ts.getFirm_score().doubleValue());
                oneFirmThemeScore.put(tsThemeId, forOneTheme);
            }

            // **step2.2**
            //raw data for one industry theme scores (based on 9 industry qns)
            if (oneIndustryThemeScore.get(tsThemeId) == null) {
                // insert
                ArrayList<Double> forOneTheme = new ArrayList<Double>();
                forOneTheme.add(ts.getInd_score().doubleValue());
                oneIndustryThemeScore.put(tsThemeId, forOneTheme);
            } else {
                // modify
                ArrayList<Double> forOneTheme = oneIndustryThemeScore.get(tsThemeId);
                forOneTheme.add(ts.getInd_score().doubleValue());
                oneIndustryThemeScore.put(tsThemeId, forOneTheme);
            }

            // **step2.3** 
            //raw data for one avg theme scores (based on 45 qns)
            if (oneAvgThemeScore.get(tsThemeId) == null) {
                // insert the new theme_id key
                ArrayList<Double> forOneTheme = new ArrayList<Double>();
                forOneTheme.add(ts.getAvg_score());
                oneAvgThemeScore.put(tsThemeId, forOneTheme);
            } else {
                // modify the existing, ++
                ArrayList<Double> forOneTheme = oneAvgThemeScore.get(tsThemeId);
                forOneTheme.add(ts.getAvg_score());
                oneAvgThemeScore.put(tsThemeId, forOneTheme);
            }
        }

        // **step2**
        //raw data for overall theme scores
        Query query = em.createQuery("SELECT a FROM Attempt a WHERE a.industry.ind_id=:p1 AND a.status=:p2");
        query.setParameter("p1", att.getIndustry().getId());
        query.setParameter("p2", Attempt.SurveyStatus.Acceptable);
        List<Attempt> acceptableAttempts = query.getResultList();

        Map<Long, ArrayList<Double>> allFirmThemeScores = new HashMap<Long, ArrayList<Double>>();
        Map<Long, ArrayList<Double>> allIndustryThemeScores = new HashMap<Long, ArrayList<Double>>();
        Map<Long, ArrayList<Double>> allAvgThemeScores = new HashMap<Long, ArrayList<Double>>();

        for (Attempt atpt : acceptableAttempts) {
            for (ThemeScore ts : atpt.getThemeScore()) {
                long tsThemeId = ts.getTheme().getId();
                // **step2.1**
                //raw data for overall firm theme scores (based on 9 firm qns)
                if (allFirmThemeScores.get(tsThemeId) == null) {
                    // insert
                    ArrayList<Double> forOneTheme = new ArrayList<Double>();
                    forOneTheme.add(ts.getFirm_score().doubleValue());
                    allFirmThemeScores.put(tsThemeId, forOneTheme);
                } else {
                    // modify
                    ArrayList<Double> forOneTheme = allFirmThemeScores.get(tsThemeId);
                    forOneTheme.add(ts.getFirm_score().doubleValue());
                    allFirmThemeScores.put(tsThemeId, forOneTheme);
                }

                // **step2.2**
                //raw data for overall industry theme scores (based on 9 industry qns)
                if (allIndustryThemeScores.get(tsThemeId) == null) {
                    // insert
                    ArrayList<Double> forOneTheme = new ArrayList<Double>();
                    forOneTheme.add(ts.getInd_score().doubleValue());
                    allIndustryThemeScores.put(tsThemeId, forOneTheme);
                } else {
                    // modify
                    ArrayList<Double> forOneTheme = allIndustryThemeScores.get(tsThemeId);
                    forOneTheme.add(ts.getInd_score().doubleValue());
                    allIndustryThemeScores.put(tsThemeId, forOneTheme);
                }

                // **step2.3** 
                //raw data for overall avg theme scores (based on 45 qns)
                if (allAvgThemeScores.get(tsThemeId) == null) {
                    // insert the new theme_id key
                    ArrayList<Double> forOneTheme = new ArrayList<Double>();
                    forOneTheme.add(ts.getAvg_score());
                    allAvgThemeScores.put(tsThemeId, forOneTheme);
                } else {
                    // modify the existing, ++
                    ArrayList<Double> forOneTheme = allAvgThemeScores.get(tsThemeId);
                    forOneTheme.add(ts.getAvg_score());
                    allAvgThemeScores.put(tsThemeId, forOneTheme);
                }
            }
        }

        // ======== generate index-16value list ========
        // **step3** insert <index, value>
        //pass in oneFirmThemeScore/oneIndustryThemeScore/oneAvgThemeScore/allFirmThemeScores/allIndustryThemeScores/allAvgThemeScores
        //## can add in more index here
        result.put(SF1IndexType.AvgIndivAvgSc.getText(), getSixteenAvgValues(oneAvgThemeScore, ComputeType.Average));
        result.put(SF1IndexType.AvgIndivFirmSc.getText(), getSixteenAvgValues(oneFirmThemeScore, ComputeType.Average));
        result.put(SF1IndexType.AvgIndivIndustrySc.getText(), getSixteenAvgValues(oneIndustryThemeScore, ComputeType.Average));
        result.put(SF1IndexType.AvgAllAvgSc.getText(), getSixteenAvgValues(allAvgThemeScores, ComputeType.Average));
        result.put(SF1IndexType.AvgAllFirmSc.getText(), getSixteenAvgValues(allFirmThemeScores, ComputeType.Average));
        result.put(SF1IndexType.AvgAllIndustrySc.getText(), getSixteenAvgValues(allIndustryThemeScores, ComputeType.Average));

        result.put(SF1IndexType.TotalIndivAvgSc.getText(), getSixteenAvgValues(oneAvgThemeScore, ComputeType.Total));
        result.put(SF1IndexType.TotalIndivFirmSc.getText(), getSixteenAvgValues(oneFirmThemeScore, ComputeType.Total));
        result.put(SF1IndexType.TotalIndivIndustrySc.getText(), getSixteenAvgValues(oneIndustryThemeScore, ComputeType.Total));
        result.put(SF1IndexType.TotalAllAvgSc.getText(), getSixteenAvgValues(allAvgThemeScores, ComputeType.Total));
        result.put(SF1IndexType.TotalAllFirmSc.getText(), getSixteenAvgValues(allFirmThemeScores, ComputeType.Total));
        result.put(SF1IndexType.TotalAllIndustrySc.getText(), getSixteenAvgValues(allIndustryThemeScores, ComputeType.Total));

//        // TODO: testing result
//        for (Entry o : result.entrySet()) {
//            for (int i = 0; i < 4; i++) {
//                for (int j = 0; j < 4; j++) {
//                    double[][] abc = (double[][])o.getValue();
//                    System.out.println("**key : " + o.getKey() + "** i = "+ i + " ** j = " + j + "  " + abc[i][j]);
//                }
//            }
//        }

        return result;
    }

    private double[][] getSixteenAvgValues(Map<Long, ArrayList<Double>> rawMap, ComputeType ct) {
        double[][] result = new double[4][4]; // length = 16

        int[] lengths = new int[9];
        int totalNumberQns = 0;
        for (Object keyId : rawMap.keySet()) {

            Long theme_id = (Long) keyId;
            ArrayList<Double> scs = (ArrayList<Double>) rawMap.get(theme_id);

            double sum = 0;
            for (Double sc : scs) {
                sum += sc;
            }

            int x = 0, y = 0;
            int normedThemeId = (theme_id.intValue() - 1) % 9;
            y = normedThemeId / 3;
            x = normedThemeId % 3;

            //## can change
            if (ct.equals(ComputeType.Average)) {
                result[x][y] = sum * 1.0 / scs.size();
            } else if (ct.equals(ComputeType.Total)) {
                result[x][y] = sum;
            }

            result[3][y] += sum;
            result[x][3] += sum;
            result[3][3] += sum;

            lengths[normedThemeId] = scs.size();
            totalNumberQns += scs.size();
        }

        //## can change
        if (ct.equals(ComputeType.Total)) {
            return result;
        }

        // compute the aggregated average value
        for (int i = 0; i < 3; i++) {
            result[i][3] = result[i][3] / (lengths[0 + i % 3] + lengths[3 + i % 3] + lengths[6 + i % 3]);
            result[3][i] = result[3][i] / (lengths[0 + i * 3] + lengths[1 + i * 3] + lengths[2 + i * 3]);
        }
        result[3][3] = result[3][3] / totalNumberQns;

        return result;
    }

    @Override
    public Map<String, Double> superComputeTwo(Map<String, double[][]> sfResults1) {
        Map<String, Double> result = new HashMap<String, Double>();

        double ans = 0;

        ans = getWeightageScoreWRTRanking(sfResults1.get(SF1IndexType.AvgIndivAvgSc.getText()));
        result.put(SF2IndexType.WeightedWRTAvgIndivAvgSc.getText(), ans);

        ans = getWeightageScoreWRTRanking(sfResults1.get(SF1IndexType.AvgIndivFirmSc.getText()));
        result.put(SF2IndexType.WeightedWRTAvgIndivFirmSc.getText(), ans);

        return result;
    }

    private double getWeightageScoreWRTRanking(double[][] sfResult) {
        double result = 0;

        Map<Integer, Double> unsortedWithSc = new HashMap<Integer, Double>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                unsortedWithSc.put(3 * j + i + 1, sfResult[i][j]);
            }
        }

        Map<Integer, Integer> sortedWithRanks = new HashMap<Integer, Integer>();

        int r = 1;
        int sumOfRanks = 0;
        double prevValue = 0;
        for (Entry<Integer, Double> entry : entriesSortedByValuesDesc(unsortedWithSc)) {
            if (prevValue != 0 && entry.getValue().doubleValue() != prevValue) {
                r++;
            }
            sortedWithRanks.put(entry.getKey(), r); // theme_pos, rank
            sumOfRanks += r;
            //System.out.println("rank ="+ r + " key ="+ entry.getKey() + " value ="+ entry.getValue());
            prevValue = entry.getValue().doubleValue();
        }

        for (Entry<Integer, Integer> entry : sortedWithRanks.entrySet()) {
            result += (entry.getValue() * 1.0 / sumOfRanks) * unsortedWithSc.get(entry.getKey());
            //System.out.println("entry value" + entry.getValue() + "entry key" + entry.getKey());
        }

        return result;
    }

    @Override
    public ArrayList<ChartsHelper> prepareRankingGraphFive(Map<String, double[][]> sfResults1, long industryId, boolean benchmarking) {
        ArrayList<ChartsHelper> result = new ArrayList<ChartsHelper>();
        double[][] indAvgScore = sfResults1.get(SF1IndexType.AvgIndivAvgSc.getText());
        double[][] indFirmScore = sfResults1.get(SF1IndexType.AvgIndivFirmSc.getText());
        double[][] indIndustrySocre = sfResults1.get(SF1IndexType.AvgIndivIndustrySc.getText());
        double[][] overallAvgScore = sfResults1.get(SF1IndexType.AvgAllAvgSc.getText());

        ChartsHelper bmch = new ChartsHelper();

        for (int i = 0; i < 5; i++) {
            ChartsHelper ch = new ChartsHelper();
            ArrayList<SubChartHelper> subLists = new ArrayList<SubChartHelper>();
            ch.setType(0);
            if (i == 0) {
                ch.setTitle(RankGraph1);
                subLists = getSubChartListForRankingGraph(indFirmScore, indAvgScore, overallAvgScore, industryId);
                // for bench marking
                bmch.setType(4);
                bmch.setTitle(RankGraphBM);
                bmch.setSubChartList(subLists);
            } else if (i == 1) {
                ch.setTitle(RankGraph2);
                subLists = getSubChartListForRankingGraph(indIndustrySocre, indAvgScore, overallAvgScore, industryId);
            } else if (i == 2) {
                ch.setTitle(RankGraph3);
                subLists = getSubChartListForRankingGraph(getMixedView(indFirmScore, indIndustrySocre, 1, 3),
                        indAvgScore, overallAvgScore, industryId);
            } else if (i == 3) {
                ch.setTitle(RankGraph4);
                subLists = getSubChartListForRankingGraph(getMixedView(indFirmScore, indIndustrySocre, 1, 1),
                        indAvgScore, overallAvgScore, industryId);
            } else if (i == 4) {
                ch.setTitle(RankGraph5);
                subLists = getSubChartListForRankingGraph(getMixedView(indFirmScore, indIndustrySocre, 3, 1),
                        indAvgScore, overallAvgScore, industryId);
            }
            ch.setSubChartList(subLists);
            result.add(ch);
        }

        if (benchmarking) {
            result.add(bmch);
        }

        return result;
    }

    private double[][] getMixedView(double[][] indivFirmScore, double[][] indivIndustrySocre,
            double firmIndex, double industryIndex) {
        double[][] result = new double[4][4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result[i][j] = indivFirmScore[i][j] * (firmIndex / (firmIndex + industryIndex))
                        + indivIndustrySocre[i][j] * (industryIndex / (firmIndex + industryIndex));
            }
        }
        return result;
    }

    private ArrayList<SubChartHelper> getSubChartListForRankingGraph(double[][] indivScore,
            double[][] indivAvgScore, double[][] overallAvgScore, long industryId) {
        ArrayList<SubChartHelper> result = new ArrayList<SubChartHelper>();

        Map<Integer, Integer> themeWithRanks = getThemeWithRanking(indivScore);

        for (Entry<Integer, Integer> entry : entriesSortedByValuesAsc(themeWithRanks)) {
            SubChartHelper sch = new SubChartHelper();
            sch.setLegend("#" + entry.getValue() + " Theme" + entry.getKey());

            int x = 0, y = 0;
            x = (entry.getKey() - 1) % 3;
            y = (entry.getKey() - 1) / 3;
            sch.setValue(indivAvgScore[x][y]);
            sch.setBenchmark(overallAvgScore[x][y]);

//            // get recommendation
//            String recommendation = "";
//            Query query = em.createQuery("SELECT t FROM Theme t WHERE t.industry_id=:p1 AND t.position=:p2");
//            query.setParameter("p1", industryId);
//            query.setParameter("p2", entry.getKey());
//            Theme th = (Theme) query.getSingleResult();
//
//            for (Recommendation rcmd : th.getRecommendation()) {
//                if (rcmd.getLower_range().doubleValue() <= sch.getValue()
//                        && rcmd.getUpper_range().doubleValue() > sch.getValue()) {
//                    recommendation += rcmd.getLong_desc();
//                    recommendation += "\n";
//                }
//            }
//            
//            sch.setContent(recommendation);

//            System.out.println("legend " + sch.getLegend()
//                    + " sch value " + sch.getValue() + " sch bm " + sch.getBenchmark());

            result.add(sch);
        }

        return result;
    }

    private Map<Integer, Integer> getThemeWithRanking(double[][] indivScore) {

        Map<Integer, Double> unsortedWithSc = new HashMap<Integer, Double>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                unsortedWithSc.put(3 * j + i + 1, indivScore[i][j]);
            }
        }

        Map<Integer, Integer> sortedWithRanks = new HashMap<Integer, Integer>();
        int r = 1;
        int sumOfRanks = 0;
        double prevValue = 0;
        for (Entry<Integer, Double> entry : entriesSortedByValuesDesc(unsortedWithSc)) {
            if (prevValue != 0 && entry.getValue().doubleValue() != prevValue) {
                r++;
            }
            sortedWithRanks.put(entry.getKey(), r); // theme_pos, rank
            sumOfRanks += r;
            //System.out.println("getThemeWithRanking: rank ="+ r + " key ="+ entry.getKey() + " value ="+ entry.getValue());
            prevValue = entry.getValue().doubleValue();
        }

        return sortedWithRanks;
    }

    @Override
    public ArrayList<ChartsHelper> prepareSixteenValueGraph(Map<String, double[][]> superComputeOne, long industryId, boolean recommendation) {
        ArrayList<ChartsHelper> result = new ArrayList<ChartsHelper>();

        for (Entry<String, double[][]> entry : superComputeOne.entrySet()) {
            ChartsHelper ch = new ChartsHelper();
            ch.setType(1);
            ch.setTitle(entry.getKey());
            ch.setSubChartList(getSubChartListForSixteenValuesGraph(entry.getValue()));
            result.add(ch);

            // check whether need to add the recommendation table

//            if (entry.getKey().equals(SF1IndexType.AvgIndivAvgSc.getText())) {
//                result.add(getRecommendationHelper(industryId, superComputeOne));
//            }
        }
        if (recommendation == true) {
            result.add(getRecommendationHelper(industryId, superComputeOne));
        }

        return result;
    }

    private ChartsHelper getRecommendationHelper(long industryId, Map<String, double[][]> sfResults1) {
        ChartsHelper ch = new ChartsHelper();
        ch.setType(2);
        ch.setTitle("Recommendations");

        ArrayList<SubChartHelper> schs = new ArrayList<SubChartHelper>();

        double[][] indivAvgScore = sfResults1.get(SF1IndexType.AvgIndivAvgSc.getText());

        for (int i = 1; i <= 9; i++) {
            SubChartHelper sch = new SubChartHelper();

            int x = 0, y = 0;
            x = (i - 1) % 3;
            y = (i - 1) / 3;
            sch.setValue(indivAvgScore[x][y]);

            // get recommendation
            String recommendation = "";
            Query query = em.createQuery("SELECT t FROM Theme t WHERE t.industry_id=:p1 AND t.position=:p2");
            query.setParameter("p1", industryId);
            query.setParameter("p2", i);
            Theme th = (Theme) query.getSingleResult();

            sch.setLegend(th.getName());

            for (Recommendation rcmd : th.getRecommendation()) {
                if (rcmd.getLower_range().doubleValue() <= sch.getValue()
                        && rcmd.getUpper_range().doubleValue() > sch.getValue()) {
                    recommendation += rcmd.getShort_desc() + ":" + "\n";
                    recommendation += rcmd.getLong_desc();
                    recommendation += "\n\n";
                }
            }

            sch.setContent(recommendation);

            schs.add(sch);
        }

        ch.setSubChartList(schs);

        return ch;
    }

    private ArrayList<SubChartHelper> getSubChartListForSixteenValuesGraph(double[][] value) {
        ArrayList<SubChartHelper> result = new ArrayList<SubChartHelper>();

        // TODO: need clarification for 16 graph

        SubChartHelper sch = new SubChartHelper();
        sch.setValue1(value[0][0]);
        sch.setValue2(value[1][0]);
        sch.setValue3(value[2][0]);
        sch.setValue4(value[3][0]);
        sch.setValue5(value[0][1]);
        sch.setValue6(value[1][1]);
        sch.setValue7(value[2][1]);
        sch.setValue8(value[3][1]);
        sch.setValue9(value[0][2]);
        sch.setValue10(value[1][2]);
        sch.setValue11(value[2][2]);
        sch.setValue12(value[3][2]);
        sch.setValue13(value[0][3]);
        sch.setValue14(value[1][3]);
        sch.setValue15(value[2][3]);
        sch.setValue16(value[3][3]);

        result.add(sch);

        return result;
    }

    @Override
    public ArrayList<ChartsHelper> prepareIndexTable(Map<String, Double> sfResults2) {
        ArrayList<ChartsHelper> result = new ArrayList<ChartsHelper>();
        ChartsHelper ch = new ChartsHelper();
        ch.setTitle("Indices");
        ch.setType(3);

        ArrayList<SubChartHelper> schs = new ArrayList<SubChartHelper>();

        for (Entry<String, Double> entry : sfResults2.entrySet()) {
            SubChartHelper sch = new SubChartHelper();

            sch.setLegend(entry.getKey());
            sch.setValue(entry.getValue());

            //System.out.println("legend for index table "+sch.getLegend() + "value for index table" + sch.getValue());

            schs.add(sch);
        }

        ch.setSubChartList(schs);

        result.add(ch);
        return result;
    }

    @Override
    public ArrayList<ChartsHelper> prepareReportGraph(Map<String, double[][]> superComputeOne, long id, boolean benchmarking, boolean recommendation) {
        ArrayList<ChartsHelper> result = prepareRankingGraphFive(superComputeOne, id, benchmarking);
        result.addAll(prepareSixteenValueGraph(superComputeOne, id, recommendation));
        result.addAll(prepareIndexTable(superComputeTwo(superComputeOne)));
        return result;
    }

    @Override
    public Attempt getAttemp(Long attemp_id) {
        Attempt att = em.find(Attempt.class, attemp_id);
        return att;
    }
}
